﻿namespace Kaesoft.Hospedaje.Excepciones
{
    using Utilidades;
    using System;
    using System.Text;
    using System.Runtime.Serialization;

    /// <summary>
    /// Excepcion gérerica
    /// </summary>
    [Serializable]
    [DataContract]
    public class ExcepcionBase : ApplicationException
    {
        #region Constantes

        private const char Igual = '=';
        private const char Espacio = ' ';
        private const int Cero = 0;
        private const string Coma = ",";

        #endregion

        #region Enumerados

        public enum Info
        {
            Culture,
            Version
        }

        #endregion

        #region Atributos

        private bool _aplicaCultura;
        private bool _aplicaVersion;
        private DateTime _fechaError;
        private DateTime _horaError;
        private readonly bool _tieneInnerException;
        private readonly string _nombreAplicacion;
        private string _culturaAplicacion = Recursos.Indefinido;
        private string _metodoAccion = Recursos.Indefinido;
        private string _nombreOrdenador = Environment.MachineName;
        private string _nombreUsuario = Environment.UserName;
        private string _rastroPila;
        private string _tipoExcepcion;
        private string _versionAplicacion = Recursos.VersionDefecto;
        private string _versionSistemaOperativo = Environment.OSVersion.ToString();

        #endregion

        #region Propiedades

        /// <summary>
        /// Seguimiento de la pila
        /// </summary>
        public override string StackTrace
        {
            get
            {
                if (_tieneInnerException == false)
                {
                    _rastroPila = base.StackTrace;
                }
                else
                {
                    _rastroPila = (InnerException != null) ? InnerException.StackTrace : string.Empty;
                }
                return _rastroPila;
            }
        }

        /// <summary>
        /// Propiedad solo de lectura que contiene la cultura de la aplicación
        /// </summary>
        public string CulturaAplicacion
        {
            get
            {
                if (_aplicaCultura == false)
                {
                    ObtenerAtributosAplicacion();
                }
                return _culturaAplicacion;
            }
        }

        /// <summary>
        /// Propiedad solo de lectura que contiene la excepcion interna
        /// </summary>
        public string ExcepcionInterna
        {
            get
            {
                return _tieneInnerException ? InnerException.ToString() : string.Empty;
            }
        }

        /// <summary>
        /// Prodiedad solo de lectura que retorna la Fecha en la que se producio el error
        /// </summary>
        public string FechaError
        {
            get
            {
                return  _fechaError.ToShortDateString();
            }
        }

        /// <summary>
        /// Propiedad solo de lectura que devuelve la hora en la que se producio el error
        /// </summary>
        public string HoraError
        {
            get
            {
                return _horaError.ToShortTimeString();
            }
        }

        /// <summary>
        /// Propiedad solo de lectura que devuelve el método que produce la excepción actual.
        /// </summary>
        public string Metodo
        {
            get
            {
                if (TargetSite == null)
                {
                    return string.Empty;
                }
                return TargetSite.Name;
            }
        }

        /// <summary>
        /// Propiedad que indica el metodo en el que se produjo el error
        /// </summary>
        public string MetodoAccion
        {
            get
            {
                return _metodoAccion;
            }
            set
            {
                _metodoAccion = value;
            }
        }

        /// <summary>
        /// Propiedad solo de lectura que retorna el nombre del ordenador en el que se produjo el error
        /// </summary>
        public string NombreOrdenador
        {
            get
            {
                return _nombreOrdenador;
            }
        }

        /// <summary>
        /// Propiedad solo de lectura que obtiene el nombre del usuario en curso
        /// </summary>
        public string NombreUsuario
        {
            get
            {
                return _nombreUsuario;
            }
        }

        /// <summary>
        /// Propiedad solo de lectura que obtiene el tipo de excepcion
        /// </summary>
        public string TipoExcepcion
        {
            get
            {
                _tipoExcepcion = ObtenerExcepcionBase();
                return _tipoExcepcion;
            }
        }

        /// <summary>
        /// Propiedad solo de lectura mediante la cual se obtiene la version de la aplicacion
        /// </summary>
        public string VersionAplicacion
        {
            get
            {
                if (_aplicaVersion == false)
                {
                    ObtenerAtributosAplicacion();
                }
                return _versionAplicacion;
            }
        }

        /// <summary>
        /// Propiedad solo de lectura mediante la cual se obtiene la version del sistema
        /// operativo correspondiente al ordenador en el que se produjo el error
        /// </summary>
        public string VersionSistemaOperativo
        {
            get
            {
                return _versionSistemaOperativo;
            }
        }

        #endregion

        #region Constructores

        /// <summary>
        /// Inicializa una instancia de la Excepción Base
        /// </summary>
        public ExcepcionBase()
        {
            InicializarVariables();
        }

        /// <summary>
        /// Inicializa una instancia de la Excepción Base
        /// </summary>
        /// <param name="message">Descripción del error producido</param>
        public ExcepcionBase(string message)
            : base(message)
        {
            InicializarVariables();
        }

        /// <summary>
        /// Inicializa una instancia de la Excepción Base
        /// </summary>
        /// <param name="message">Descripción del error producido</param>
        /// <param name="exception">Excepción producida</param>
        public ExcepcionBase(string message, Exception exception)
            : base(message, exception)
        {
            InicializarVariables();
            _tieneInnerException = true;
        }

        /// <summary>
        /// Inicializa una instancia de la Excepción Base
        /// </summary>
        /// <param name="message">Descripción del error producido</param>
        /// <param name="innerException">Excepción interna</param>
        public ExcepcionBase(string message, SystemException innerException)
            : base(message, innerException)
        {
            InicializarVariables();
            _tieneInnerException = true;
        }

        /// <summary>
        /// Inicializa una instancia de la Excepción Base
        /// </summary>
        /// <param name="metodoAccion">Nombre del método en el que se produjo el error</param>
        /// <param name="mensaje">Descripción del error producido</param>
        public ExcepcionBase(string metodoAccion, string mensaje)
            : base(mensaje)
        {
            _metodoAccion = metodoAccion;
        }

        /// <summary>
        /// Inicializa una instancia de la Excepción Base
        /// </summary>
        /// <param name="metodoAccion">Nombre del método en el que se produjo el error</param>
        /// <param name="mensaje">Descripción del error producido</param>
        /// <param name="excepcion">Excepción producida</param>
        public ExcepcionBase(string metodoAccion, string mensaje, SystemException excepcion)
            : base(mensaje, excepcion)
        {
            _metodoAccion = metodoAccion;
            _tieneInnerException = true;
        }

        /// <summary>
        /// Inicializa una instancia de la Excepción Base
        /// </summary>
        /// <param name="info">Información serializada</param>
        /// <param name="contexto">
        /// Objeto que describe el orígen y el destino de una secuencia serializada
        /// determinada, proporciona un invocador adicional definido en el contexto.
        /// </param>
        public ExcepcionBase(SerializationInfo info, StreamingContext contexto)
            : base(info, contexto)
        {
            _culturaAplicacion = info.GetString("CulturaAplicacion");
            _versionAplicacion = info.GetString("VersionAplicacion");
            _aplicaCultura = info.GetBoolean("BanderaAplicaCultura");
            _aplicaVersion = info.GetBoolean("BanderaAplicaVersion");
            _metodoAccion = info.GetString("MetodoAccion");
            _nombreOrdenador = info.GetString("NombreOrdenador");
            _nombreUsuario = info.GetString("NombreUsuario");
            _versionSistemaOperativo = info.GetString("VersionSistemaOperativo");
            _fechaError = info.GetDateTime("FechaError");
            _horaError = info.GetDateTime("HoraError");
            _nombreAplicacion = info.GetString("NombreAplicacion");
            _tipoExcepcion = info.GetString("TipoExcepcion");
            _rastroPila = info.GetString("StackTrace");
        }

        #endregion

        #region Metodos Publicos

        /// <summary>
        /// Sobreescribimos el método ToString
        /// </summary>
        /// <returns>
        /// Retorna un string que contiene la decripción del error
        /// </returns>
        public override string ToString()
        {
            var mensaje = new StringBuilder();
            mensaje.AppendFormat("Objeto de origen : {1}{0}Método de Origen : {2}{0}Método de acción : {3}",
                Environment.NewLine, Source, Metodo, MetodoAccion);
            return mensaje.ToString();
        }

        /// <summary>
        /// Agregamos informacion a un objeto de serialización
        /// </summary>
        /// <param name="info">Objeto a agregar la información</param>
        /// <param name="context">
        /// Objeto que describe el orígen y el destino de una secuencia serializada
        /// determinada, proporciona un invocador adicional definido en el contexto.
        /// </param>
        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);
            info.AddValue("CulturaAplicacion", _culturaAplicacion);
            info.AddValue("VersionAplicacion", _versionAplicacion);
            info.AddValue("BanderaAplicaCultura", _aplicaCultura);
            info.AddValue("BanderaAplicaVersion", _aplicaVersion);
            info.AddValue("MetodoAccion", _metodoAccion);
            info.AddValue("NombreOrdenador", _nombreOrdenador);
            info.AddValue("NombreUsuario", _nombreUsuario);
            info.AddValue("VersionSistemaOperativo", _versionSistemaOperativo);
            info.AddValue("FechaError", _fechaError);
            info.AddValue("HoraError", _horaError);
            info.AddValue("NombreAplicacion", _nombreAplicacion);
            info.AddValue("TipoExcepcion", _tipoExcepcion);
            info.AddValue("StackTrace", _rastroPila);
        }

        /// <summary>
        /// Función para recuperar la base de la excepción
        /// </summary>
        /// <returns>El tipo de la excepción base</returns>
        public string ObtenerExcepcionBase()
        {
            return GetBaseException().ToString();
        }

        /// <summary>
        /// Método que busca un atributo en una cadena ingresada
        /// </summary>
        /// <param name="informacion">Información en la cual se buscará el atributo</param>
        /// <param name="atributo">Atributo a buscarse</param>
        /// <returns>Retorna la opcion de la versión</returns>
        public string ObtenerAtributo(string informacion, Info atributo)
        {
            var info = string.Empty;
            if (informacion.IndexOf(atributo.ToString(), Cero) >= Cero)
            {
                var atributos = informacion.Split(Igual);
                info = atributos[1];
                if (info.IndexOf(Coma) >= Cero)
                {
                    info = info.Substring(Cero, info.IndexOf(Coma));
                }
            }
            return info;
        }

        #endregion

        #region Metodos Privados

        /// <summary>
        /// Proceso de inicializacion de variables
        /// </summary>
        private void InicializarVariables()
        {
            _versionAplicacion = Recursos.VersionDefecto;
            _culturaAplicacion = Recursos.Indefinido;
            _metodoAccion = Recursos.Indefinido;
            _nombreUsuario = Environment.UserName;
            _nombreOrdenador = Environment.MachineName;
            _versionSistemaOperativo = Environment.OSVersion.ToString();
            _aplicaVersion = false;
            _aplicaCultura = false;
            _tipoExcepcion = string.Empty;
            _rastroPila = null;
            ObtenerAtributosAplicacion();
        }

        /// <summary>
        /// Obtención de atributos de la aplicación
        /// </summary>
        private void ObtenerAtributosAplicacion()
        {
            if (_tieneInnerException == false)
            {
                if (TargetSite != null)
                {
                    if (TargetSite.ReflectedType.AssemblyQualifiedName != null)
                    {
                        ObtenerVersionCultura(TargetSite.ReflectedType.AssemblyQualifiedName);
                    }
                }
            }
            else
            {
                if (InnerException != null)
                {
                    if (InnerException.TargetSite != null)
                    {
                        if (InnerException.TargetSite.ReflectedType.AssemblyQualifiedName != null)
                        {
                            ObtenerVersionCultura(InnerException.TargetSite.ReflectedType.AssemblyQualifiedName);
                        }
                    }
                }
            }
            _fechaError = DateTime.Today;
            _horaError = DateTime.Now;
        }

        /// <summary>
        /// Obtenemos la version y cultura de la aplicación
        /// </summary>
        /// <param name="nombreEnsamblado">Nombre del ensamblado</param>
        private void ObtenerVersionCultura(string nombreEnsamblado)
        {
            var ensamblado = nombreEnsamblado.Split(Espacio);

            foreach (var item in ensamblado)
            {
                if (!_aplicaVersion)
                {
                    _versionAplicacion = ObtenerAtributo(item, Info.Version);
                    if (string.IsNullOrEmpty(_versionAplicacion))
                    {
                        _versionAplicacion = Recursos.Indefinido;
                    }
                    else
                    {
                        _aplicaVersion = true;
                    }
                }
                if (_aplicaCultura == false)
                {
                    _culturaAplicacion = ObtenerAtributo(item, Info.Culture);
                    if (string.IsNullOrEmpty(_culturaAplicacion))
                    {
                        _culturaAplicacion = Recursos.Indefinido;
                    }
                    else
                    {
                        _aplicaCultura = true;
                    }
                }
                if (_aplicaVersion & _aplicaCultura)
                {
                    break;
                }
            }
        }

        #endregion
    }
}